package com.ala4.oxcafe.common.utils;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Objects;

/**
 * 异常处理工具类 - 提取根因异常
 *
 * @author PING
 * @version 1.0.0
 * @date 2025/8/4 12:54
 */
public class ExceptionUtil {

    /**
     * 获取根因异常（最底层的原始异常）
     *
     * @param throwable 上层异常
     * @return 最原始的异常对象，如果没有下层异常则返回输入异常本身
     */
    public static Throwable getRootCause(Throwable throwable) {
        if (throwable == null) {
            return null;
        }

        // 避免循环引用导致的无限循环
        final int maxDepth = 1000;
        int depth = 0;

        Throwable rootCause = throwable;
        while (true) {
            Throwable cause = rootCause.getCause();
            // 达到最大深度或没有更深层异常时终止
            if (cause == null || rootCause == cause || depth++ >= maxDepth) {
                break;
            }
            rootCause = cause;
        }
        return rootCause;
    }

    /**
     * 获取根因异常的错误信息
     *
     * @param throwable 上层异常
     * @return 最原始异常的错误信息，如果原始异常没有信息则返回其类名
     */
    public static String getRootCauseMessage(Throwable throwable) {
        Throwable root = getRootCause(throwable);
        if (root == null) {
            return "";
        }

        String message = root.getMessage();
        if (message != null && !message.isEmpty()) {
            return message;
        }
        return "[" + root.getClass().getSimpleName() + "]";
    }

    /**
     * 获取完整的异常链信息（包含代码位置）
     *
     * @param throwable 上层异常
     * @return 包含所有异常层级及代码位置的字符串
     */
    public static String getFullExceptionChain(Throwable throwable) {
        if (throwable == null) {
            return "";
        }

        StringBuilder sb = new StringBuilder();
        Throwable current = throwable;
        int indent = 0;
        boolean isFirst = true;

        while (current != null) {
            // 添加缩进表示层级关系
            for (int i = 0; i < indent; i++) {
                sb.append("  ");
            }

            if (isFirst) {
                sb.append("Exception: ");
                isFirst = false;
            } else {
                sb.append("Caused by: ");
            }

            sb.append(current.getClass().getName())
                    .append(": ")
                    .append(Objects.toString(current.getMessage(), "<no message>"))
                    .append("\n");

            // 添加第一个堆栈跟踪元素（抛出位置）
            StackTraceElement[] stackTrace = current.getStackTrace();
            if (stackTrace != null && stackTrace.length > 0) {
                for (int i = 0; i < indent + 1; i++) {
                    sb.append("  ");
                }
                StackTraceElement firstElement = stackTrace[0];
                sb.append("at ")
                        .append(firstElement.getClassName())
                        .append(".")
                        .append(firstElement.getMethodName())
                        .append("(")
                        .append(firstElement.getFileName())
                        .append(":")
                        .append(firstElement.getLineNumber())
                        .append(")\n");
            }

            current = current.getCause();
            indent++;

            // 安全控制：防止异常链循环
            if (indent > 1000) {
                for (int i = 0; i < indent; i++) {
                    sb.append("  ");
                }
                sb.append("... (exception chain too deep)");
                break;
            }
        }
        return sb.toString();
    }

    /**
     * 获取完整的堆栈跟踪信息（包含所有堆栈元素）
     *
     * @param throwable 异常对象
     * @return 完整的堆栈跟踪字符串
     */
    public static String getFullStackTrace(Throwable throwable) {
        if (throwable == null) {
            return "";
        }
        StringWriter sw = new StringWriter();
        throwable.printStackTrace(new PrintWriter(sw));
        return sw.toString();
    }

    /**
     * 获取根因异常的代码位置
     *
     * @param throwable 上层异常
     * @return 根因异常的抛出位置，格式：类名.方法名(文件名:行号)
     */
    public static String getRootCauseLocation(Throwable throwable) {
        Throwable root = getRootCause(throwable);
        if (root == null) {
            return "";
        }

        StackTraceElement[] stackTrace = root.getStackTrace();
        if (stackTrace == null || stackTrace.length == 0) {
            return "[No stack trace available]";
        }

        StackTraceElement firstElement = stackTrace[0];
        return firstElement.getClassName() + "." +
                firstElement.getMethodName() + "(" +
                firstElement.getFileName() + ":" +
                firstElement.getLineNumber() + ")";
    }
}